import { NullMedia } from './nullMedia'
import { Base } from './base'
import { Track, VideoTrack } from './track'

export class Timeline extends Base {
  private _timeline: any
  private _nullMedia: NullMedia
  private _currentTime = 0
  private _duration = 0
  private _playbackRate = 1
  private _paused = true
  private _waiting = false
  private _dirty = false
  private _volume = 1
  private _tracks: Array<Track> = [new Track({ type: 1, children: [] })]

  constructor() {
    super()
    this._nullMedia = new NullMedia()
    this._nullMedia.on('timeupdate', (event: NullMedia) => {
      if (event.currentTime <= this._duration) {
        this._currentTime = event.currentTime
        this.emit('timeupdate', this)
        this.tracks.forEach((i) => i.onTimeupdate(this._currentTime))
      } else if (!this._paused) {
        this.pause()
      }
    })
    this._loop()
  }

  private _loop() {
    // 在时间片做统一处理
    if (this._dirty) {
      this.emit('change', this)
      this._dirty = false
    }
    requestAnimationFrame(this._loop.bind(this))
  }

  get currentTime() {
    return this._currentTime
  }

  set currentTime(val: number) {
    if (val <= this._duration) {
      this._currentTime = val
      this._tracks.forEach((t) => (t.currentTime = val))
      this._nullMedia.currentTime = val
      // do seek
      this.emit('timeupdate', this)
      if (!this._paused) {
        this.pause()
      }
    }
  }

  get playbackRate() {
    return this._playbackRate
  }

  set playbackRate(val: number) {
    this._playbackRate = val
  }

  get volume() {
    return this._volume
  }

  set volume(val: number) {
    this._volume = val
  }

  get paused() {
    return this._paused
  }

  get tracks() {
    return this._tracks
  }

  get duration() {
    return this._duration
  }

  private _handleWaiting(event: Track) {
    if (!this._waiting) {
      this._waiting = true
      if (!this._paused) {
        // this._tracks.map((t) => t.pause())
        this.currentTime = event.currentTime
      }
      // check can play
    }
  }

  private _handleCanplay(event: Track) {
    if (this._waiting && this.canplay) {
      this._waiting = false
      if (!this._paused) {
        this._tracks.map((t) => t.play())
      }
    }
  }

  get canplay() {
    return this._tracks.every((t) => t.canplay)
  }

  reset() {
    this._currentTime = 0
    this._playbackRate = 1
  }

  load(timelineJson: any): void {
    this.reset()
    this.attach(timelineJson)
    this.emit('load', this)
  }

  attach(timelineJson: any): void {
    this._timeline = timelineJson
    this._tracks = timelineJson.tracks.map((i: any) => new VideoTrack(i))
    this._duration = Math.max(...this._tracks.map((i) => i.duration))
    this.emit('durationchange', this)
    this._tracks.forEach((t) => {
      t.on('timeupdate', (event: Track) => {
        // const diff = Math.abs(this.currentTime - event.currentTime)
        this._currentTime = event.currentTime
        this.emit('timeupdate', this)
        // if (diff > 0.04) {
        //   // need to sync time
        //   console.warn('need to sync time')
        // }load
      })
      t.on('waiting', (event: Track) => {
        this._handleWaiting(event)
      })
      t.on('canplay', (event: Track) => {
        this._handleCanplay(event)
      })
      t.on('durationchange', (event: Track) => {
        this._duration = Math.max(...this._tracks.map((i) => i.duration))
        this.emit('durationchange', this)
      })
      t.on('change', (event: Track) => {
        this._dirty = true
      })
    })
  }

  toJson(): any {
    return {
      format: {
        width: 1920,
        height: 1080,
      },
      duration: this._duration,
      groups: [],
      tracks: this._tracks.map((i) => i.toJson()),
    }
  }

  play() {
    if (this._paused) {
      this._paused = false
      this.emit('play', this)
      this._nullMedia.play()
      return Promise.all(this._tracks.map((t) => t.play()))
    }
  }

  pause() {
    if (!this._paused) {
      this._paused = true
      this.emit('pause', this)
      this._nullMedia.pause()
      return Promise.all(this._tracks.map((t) => t.pause()))
    }
  }
}
